package taskprocess

import (
	"acs/comet/client"
	"acs/comet/proto"
	"acs/pbmodel"
	"acs/taskprocess/storage"
	"sync"
	"time"

	"gopkg.in/mgo.v2/bson"
	"gopkg.in/mgo.v2"
)

// taskLoopCheckTickTime 任务信息循环检测间隔时间. 单位： 秒.
const taskLoopCheckTickTime = 10

var loopTaskQuotaQueue = make(chan int, 50000)

func getLoopTaskCheckQuota() {
	loopTaskQuotaQueue <- 1
}

func releaseLoopTaskCheckQuota() {
	<-loopTaskQuotaQueue
}

func taskLoopCheck() {
	wg := new(sync.WaitGroup)
	ticker := time.NewTicker(time.Second * time.Duration(taskLoopCheckTickTime))
	for {
		clients.MapFunc(checkClientAndProcessTask, wg)
		time.Sleep(time.Millisecond * 200)
		wg.Wait()
		<-ticker.C
	}
}

// FindPrePatchByLPID 根据当前的pathid查找前置补丁.
func FindPrePatchByLPID(bundleid, lpid string, platform AppPlatform) (content *PatchTaskPersist, err error) {
	ses, err := getDb()
	if err != nil {
		return
	}
	content = new(PatchTaskPersist)
	ses.C(DbCollectionTaskContents).Find(bson.M{
		"content.bundleid":    bundleid,
		"content.platform":    platform,
		"content.prevpatchid": lpid,
	}).One(content)
	return
}

// FindPreSchemaByLSID 根据当前的schemaid查找前置补丁.
func FindPreSchemaByLSID(bundleid, lsid string, platform AppPlatform) (content *SchemaTaskPersist, err error) {
	ses, err := getDb()
	if err != nil {
		return
	}
	content = new(SchemaTaskPersist)
	ses.C(DbCollectionTaskContents).Find(bson.M{
		"content.bundleid":     bundleid,
		"content.platform":     platform,
		"content.prevschemaid": lsid,
	}).One(content)
	return
}

// checkClientAndProcessTask 检查客户端信息，判断是否有需要推送的任务，有的话立即推送任务.
func checkClientAndProcessTask(c *client.Client, wgI interface{}) {
	var err error
	defer func() {
		if err != nil && err != storage.ErrDataNotFound {
			logger.Warnf("Client tasks check Err: %v", err)
		}
	}()
	wg := wgI.(*sync.WaitGroup)
	wg.Add(1)
	getLoopTaskCheckQuota()
	defer releaseLoopTaskCheckQuota()
	defer wg.Done()
	ses, err := getDb()
	if err != nil {
		return
	}
	var patchTaskForAll, schemaTaskForAll, schemaTask, patchTask *UserTaskPersist
	userInfo := c.GetRegisterInfo()
	var cnt int
	// 匹配所有用户的path任务
	query := ses.C(DbCollectionUserTasks).Find(bson.M{"uid": "*", "type": TaskTypePatch, "platform": userInfo.System, "bundleid": userInfo.BundleID}).Sort("-taskno")
	cnt, err = query.Count()
	if err != nil && err != storage.ErrDataNotFound {
		return
	} else if cnt > 0 {
		patchTaskForAll = new(UserTaskPersist)
		err = query.One(patchTaskForAll)
		if err != nil {
			return
		}
	}

	// 匹配所有用户的schema更新任务
	query = ses.C(DbCollectionUserTasks).Find(bson.M{"uid": "*", "type": TaskTypeSchema, "platform": userInfo.System, "bundleid": userInfo.BundleID}).Sort("-taskno")
	cnt, err = query.Count()
	if err != nil && err != storage.ErrDataNotFound {
		return
	} else if cnt > 0 {
		schemaTaskForAll = new(UserTaskPersist)
		err = query.One(schemaTaskForAll)
		if err != nil {
			return
		}
	}

	if userInfo.UID != nil && *userInfo.UID != "" {
		// 匹配所当前用户的schema更新任务
		query = ses.C(DbCollectionUserTasks).Find(bson.M{"uid": c.GetRegisterInfo().UID, "type": TaskTypeSchema, "platform": userInfo.System, "bundleid": userInfo.BundleID}).Sort("-taskno")
		cnt, err = query.Count()
		if err != nil && err != storage.ErrDataNotFound {
			return
		} else if cnt > 0 {
			schemaTask = new(UserTaskPersist)
			err = query.One(schemaTask)
			if err != nil {
				return
			}
		}
		// 匹配所当前用户的patch任务
		query = ses.C(DbCollectionUserTasks).Find(bson.M{"uid": c.GetRegisterInfo().UID, "type": TaskTypePatch, "platform": userInfo.System, "bundleid": userInfo.BundleID}).Sort("-taskno")
		cnt, err = query.Count()
		if err != nil && err != storage.ErrDataNotFound {
			return
		} else if cnt > 0 {
			patchTask = new(UserTaskPersist)
			err = query.One(patchTask)
			if err != nil {
				return
			}
		}
	}

	var destSchemTask *UserTaskPersist
	if schemaTask != nil {
		if schemaTaskForAll == nil || schemaTask.TaskNo > schemaTaskForAll.TaskNo {
			destSchemTask = schemaTask
		} else {
			destSchemTask = schemaTaskForAll
		}
	} else if schemaTaskForAll != nil {
		destSchemTask = schemaTaskForAll
	}

	var destPatchTask *UserTaskPersist
	if patchTask != nil {
		if patchTaskForAll == nil || patchTask.TaskNo > patchTaskForAll.TaskNo {
			destPatchTask = patchTask
		} else {
			destPatchTask = patchTaskForAll
		}
	} else if patchTaskForAll != nil {
		destPatchTask = patchTaskForAll
	}

	var patchTaskContent *PatchTaskPersist
	if destPatchTask != nil {
		patchTaskContent = new(PatchTaskPersist)
		err = ses.C(DbCollectionTaskContents).Find(bson.M{"taskno": destPatchTask.TaskNo}).One(patchTaskContent)
		if err != nil {
			if err != storage.ErrDataNotFound {
				return
			} else {
				patchTaskContent = nil
			}
		}
	}

	var schemaTaskContent *SchemaTaskPersist
	if destSchemTask != nil {
		schemaTaskContent = new(SchemaTaskPersist)
		err = ses.C(DbCollectionTaskContents).Find(bson.M{"taskno": destSchemTask.TaskNo}).One(schemaTaskContent)
		if err != nil {
			if err != storage.ErrDataNotFound {
				return
			} else {
				schemaTaskContent = nil
			}
		}
	}

	if patchTaskContent != nil {
		if patchTaskContent.Content.PrevPatchID != "*" {
			// 如果不适合当前版本，则为客户端查找上一个符合的版本进行推送.
			if patchTaskContent.Content.PrevPatchID != *userInfo.LPID {
				var content *PatchTaskPersist
				logger.Debugf("Client[%v:%v] current LPID[%v] does not match precondition patch[%v], try to find the matchable previous patch task...", userInfo.UUID, userInfo.UID, userInfo.LPID, patchTaskContent.Content.PrevPatchID)
				content, err = FindPrePatchByLPID(*userInfo.BundleID, *userInfo.LPID, AppPlatform(*userInfo.System))
				if content != nil {
					logger.Debugf("Client[%v:%v] current LPID[%v] does not match precondition[%v] , but found matchable patch[%v] as previous", userInfo.UUID, userInfo.UID, userInfo.LPID, content.Content.PrevPatchID, content.Content.PatchID)
					patchTaskContent = content
				} else {
					patchTaskContent = nil
					logger.Warnf("Client[%v:%v] current LPID[%v] cannot find matchable previous patch.", userInfo.UUID, userInfo.UID, userInfo.LPID)
				}
			}

		}
		if patchTaskContent != nil {
			logger.Debugf("sending patch task[%v:%v] to client[%v:%v]...", patchTaskContent.Content.PatchID, patchTaskContent.TaskNo, userInfo.UUID, userInfo.UID)
			patchResp := c.SendCmd(proto.CmdPatch, &patchTaskContent.Content.Patch, &pbmodel.PatchResp{})
			if patchResp.Err != nil {
				logger.Warnf("Failed to send cmd[%v] to client [%v:%v], Err: %v", proto.CmdPatch, userInfo.UUID, userInfo.UID, patchResp.Err)
			}
		}
	}

	if schemaTaskContent != nil {
		if schemaTaskContent.Content.PrevSchemaID != "*" {
			if schemaTaskContent.Content.PrevSchemaID != *userInfo.LSID {
				var content *SchemaTaskPersist
				logger.Debugf("Client[%v:%v] current LSID[%v] does not match precondition schema[%v], try to find the matchable previous schema task...", userInfo.UUID, userInfo.UID, userInfo.LSID, schemaTaskContent.Content.PrevSchemaID)
				content, err = FindPreSchemaByLSID(*userInfo.BundleID, *userInfo.LSID, AppPlatform(*userInfo.System))
				if content != nil {
					logger.Debugf("Client[%v:%v] current LSID[%v] does not match precondition[%v] , but found matchable schema[%v] as previous", userInfo.UUID, userInfo.UID, userInfo.LSID, content.Content.PrevSchemaID, content.Content.SchemaID)
					schemaTaskContent = content
				} else {
					schemaTaskContent = nil
					logger.Warnf("Client[%v:%v] current LSID[%v] cannot find matchable previous schema.", userInfo.UUID, userInfo.UID, userInfo.LSID)
				}
			}
		}
		if schemaTaskContent != nil {
			logger.Debugf("sending schema task[%v:%v] to client[%v:%v]...", schemaTaskContent.Content.SchemaID, schemaTaskContent.TaskNo, userInfo.UUID, userInfo.UID)
			patchResp := c.SendCmd(proto.CmdSchema, &schemaTaskContent.Content.Schema, &pbmodel.SchemaResp{})
			if patchResp.Err != nil {
				logger.Warnf("Failed to send cmd[%v] to client [%v:%v], Err: %v", proto.CmdSchema, userInfo.UUID, userInfo.UID, patchResp.Err)
			}
		}
	}

	CheckEvent(ses, &userInfo, c)
}

//获取event 消息进行推送
func CheckEvent(ses *mgo.Database,userInfo *pbmodel.RegisterInfo, c *client.Client)   {
	//uid
	checkEventNotAsterisk (ses,userInfo, c)
	checkEventAsterisk(ses,userInfo, c)
}

func checkEventNotAsterisk (ses *mgo.Database,userInfo *pbmodel.RegisterInfo, c *client.Client)  {
	var err error
	//匹配当前用户未发送成功的任务
	var userTaskForAll []UserTaskPersist
	err = ses.C(DbCollectionUserTasks).Find(bson.M{"uid": *userInfo.UID, "type": TaskTypeEvent, "status": 0}).All(&userTaskForAll)
	if err != nil {
		return
	}
	//当前未发送消息的id
	for _, v := range  userTaskForAll {
		if !filterRegInfo(&v, userInfo) {
			continue
		}
		eventTaskPersist := new(EventTaskPersist)
		err = ses.C(DbCollectionTaskContents).Find(bson.M{"type": TaskTypeEvent, "taskno":v.TaskNo, "content.allsend": 0, "content.expiration":bson.M{"$gt": time.Now().Unix()} }).One(eventTaskPersist)
		if err != nil {
			if err == storage.ErrDataNotFound { //not found
				//TODO 如果没有找到讲uid 的status 状态改成2, 2表示过期
				continue //如果没找到可能就是这个消息过期了，那么就不用管了 TODO 如果这里数据太多可能会导致无用循环过多
			} else {
				logger.Warnf("mongo failed Err: %v",  err)
				return
			}
		}
		logger.Debugf("sending event task[%v] to client[%v:%v]...",  eventTaskPersist.TaskNo, userInfo.UUID, userInfo.UID)
		eventResp := c.SendCmd(proto.CmdEvent, &eventTaskPersist.Content.Event, &pbmodel.EventResp{})
		if eventResp.Err != nil {
			logger.Warnf("Failed to send cmd[%v] to client [%v:%v], Err: %v", proto.CmdEvent, userInfo.UUID, userInfo.UID, eventResp.Err)
		}else {
			//将当前未发送状态改为已发送
			err := ses.C(DbCollectionUserTasks).Update(bson.M{"uid": userInfo.UID, "type": TaskTypeEvent, "taskno":v.TaskNo}, bson.M{"$set": bson.M{"status": 1}})
			if err != nil {
				//TODO 如果更新不成功可能会造成重复发送的情况
				logger.Warnf("Failed to remove eventTask[%v] status: %v Err: %v",  userInfo.UID, 0, eventResp.Err)
				return
			}
		}
	}
}

func checkEventAsterisk (ses *mgo.Database,userInfo *pbmodel.RegisterInfo, c *client.Client)  {
	var err error
	//匹配*任务,状态为1，比当前时间大的任务
	var eventTaskPersist []EventTaskPersist
	err = ses.C(DbCollectionTaskContents).Find(bson.M{"type": TaskTypeEvent, "content.allsend": 1, "content.expiration":bson.M{"$gt": time.Now().Unix()}}).All(&eventTaskPersist)
	if err != nil {
		return
	}

	//TODO 这里还有一种做法是查出全部，自己在这里遍历uid,这里如果uid很多，可能效率并没有查两次mongo数据效率高
	for _, v := range  eventTaskPersist {
		userTaskPersist := new(UserTaskPersist)
		err = ses.C(DbCollectionUserTasks).Find(bson.M{"uid": *userInfo.UID, "type": TaskTypeEvent, "taskno":v.TaskNo, "status": 1}).One(userTaskPersist)
		if err != nil {
			if err == storage.ErrDataNotFound { //not found
				err = ses.C(DbCollectionUserTasks).Find(bson.M{"uid": "*", "type": TaskTypeEvent, "taskno":v.TaskNo}).One(userTaskPersist) //查一个已发送用来获取条件
				if err != nil  {
					if err == storage.ErrDataNotFound  {
						//TODO 因为全局存的时候存入一个*的记录 所以必然存在，不存在则存储过程存在问题
					}else {
						logger.Warnf("mongo failed Err: %v",  err)
						return
					}
				}
				//如果没有查出来, 就发送
				if !filterRegInfo(userTaskPersist, userInfo) {
					continue
				}

				logger.Debugf("sending event task[%v] to client[%v:%v]...",  userTaskPersist.TaskNo, userInfo.UUID, userInfo.UID)
				eventResp := c.SendCmd(proto.CmdEvent, &v.Content.Event, &pbmodel.EventResp{})
				if eventResp.Err != nil {
					//失败了不管，等待下次轮询
					logger.Warnf("Failed to send cmd[%v] to client [%v:%v], Err: %v", proto.CmdEvent, userInfo.UUID, userInfo.UID, eventResp.Err)
				}else {
					userTaskPersist = &UserTaskPersist{
						Type:     TaskTypeEvent,
						UID:      *userInfo.UID,
						TaskNo:   v.TaskNo,
						Status:   1,

						Appver: userTaskPersist.Appver,
						BundleIds: userTaskPersist.BundleIds,
						System: userTaskPersist.System,
						OSVersion: userTaskPersist.OSVersion,
						Brand: userTaskPersist.Brand,
						Model: userTaskPersist.Model,

					}
					err := ses.C(DbCollectionUserTasks).Insert(userTaskPersist)
					if err != nil {
						//TODO 插入不成功可能造成重复发送
						logger.Warnf("Failed to insert eventTask[%v] status: %v Err: %v",  userInfo.UID, 0, err)
						continue
					}
				}
			} else {
				logger.Warnf("mongo failed Err: %v",  err)
				//如果是查询错误
				return
			}
		}else {
			//查出来的是已发送的，就不用处理了
		}

	}
}


func filterRegInfo(task *UserTaskPersist, regInfo *pbmodel.RegisterInfo) (isTargetClient bool)  {

	//appver start  *就不进行过滤了
	if task.Appver[0] != "*" && VersionOrdinal(task.Appver[0]) > VersionOrdinal(*regInfo.Appver) {
		return
	}
	//appver end
	if task.Appver[1] != "*" && VersionOrdinal(task.Appver[1]) < VersionOrdinal(*regInfo.Appver){
		return
	}

	//bundleid 等于0的情况为没有过滤条件
	if len(task.BundleIds) > 0 {
		if ok, _ :=  InArrayString(*regInfo.BundleID, task.BundleIds); !ok {
			return
		}
	}

	//system
	if len(task.System) > 0 {
		if ok, _ := InArrayInt(int(*regInfo.System), task.System); !ok {
			return
		}
	}

	//osversion start
	if task.OSVersion[0] != "*" && VersionOrdinal(task.OSVersion[0]) > VersionOrdinal(*regInfo.OSVersion) {
		return
	}
	//osversion end
	if task.OSVersion[1] != "*" && VersionOrdinal(task.OSVersion[1]) < VersionOrdinal(*regInfo.OSVersion){
		return
	}

	//brand
	if len(task.Brand) > 0 {
		if ok, _ := InArrayString(*regInfo.Brand, task.Brand); !ok {
			return
		}
	}

	//model
	if len(task.Model) > 0 {
		if ok, _ := InArrayString(*regInfo.Model, task.Model); !ok {
			return
		}
	}

	isTargetClient = true

	return
}
